{
        File:                   Hub_RAM_Tester_1.0.spin
        Date:                   March 9, 2012
        Author[s]:              Daniel Harris
        Company:                Parallax Semiconductor

        Description:

        This is a standalone hub RAM tester for the P8X32A.  It excercises every bit in the Propeller's hub RAM.
        If there is a memory error, this test does not record where the memory error occured - it simply reports
        the cog's failure and hangs the cog.

        To see pass or fail messages, set your serial terminal to 115_200 baud, or you can chose a baud rate by
        setting the constant variable BAUDRATE.  You can also set which IO pin you want to communicate on by
        setting the TX_PIN constant below.
        
        
}
con

  _clkfreq = 80_000_000
  _clkmode = xtal1 + pll16x

  TX_PIN        = 30
  BAUDRATE      = 115_200

pub go

  'set a synchronization point in time.
  sync_start := cnt + clkfreq*2

  'launch each cog in decending order
  coginit(7, @Entry, 0)
  coginit(6, @Entry, 0)
  coginit(5, @Entry, 0)
  coginit(4, @Entry, 0)
  coginit(3, @Entry, 0)
  coginit(2, @Entry, 0)
  coginit(1, @Entry, 0)
  coginit(0, @Entry, 0)

  '^^^^  Even though the cogs are launched in decending order, the test is
  '  performed in ascending order (cog 0 tests first with cog 7 testing last)
     
  
dat

        'assembly program to write a bit pattern out to
        'every Hub RAM address and verify it

              org

Entry         waitcnt   sync_start,#0           'syncronization point for all cogs, wait here 'till it's time

              'All cogs are synchronized at this point!


              'Add an additional starting delay here.
              
              'mov       t0,one_sec             
              'shl       t0,#2
              'add       sync_start,t0


              'Calculate a time period to wait before starting the test so as not to
              'walk all over an adjacent cog's test.  This time period is based off of
              'the cog's ID.
              
              cogid     cog_num                 'Save the cog ID

              mov       t0,cog_num              'Copy the cog ID to be able to work with it
              add       t0,#1                   'Increment the value to avoid no time delay being added
              mov       timer,#0                'Zero out the timer variable

              mov       t1,one_sec              'one second...
              shr       t1,#3                   '...divided by 8 (to overlap execution a bit)
                            
:loop         add       timer,t1                'simple multiplication
              djnz      t0,#:loop

              
              add       timer,cnt              
              waitcnt   timer,#0                'do the actual wait


:main         mov       a0,#cog_num_str         'a0 = address of string to print              
              call      #Print_Str              'print the Cog number string

              
              mov       acc,cog_num             'get ready to print the cog number
              add       acc,#$30                'add this constant to make the decimal number an ASCII char
              call      #Serout                 'print the number

              
              call      #Do_Test                'Do the test!

              
              mov       acc,#13                 'print a \n
              call      #Serout

              'jmp       #blink_leds            'an alternate end to the test.  Blinks an LED that corresponds with a passed cog.
              jmp       #hang                   'end of the test, stop the cog

                                    
              
'================================ Do Test =======================================================================
'       Does the Hub RAM test.  This test writes a pattern out to Hub RAM, reads it back, and compares the results.
'       A total of 4 write/read combinations are performed on each Hub RAM location, excercising each bit of the
'       memory.
       
Do_Test       mov       curr_addy,#0            'setup the test.  Start testing from the beginning of memory.

:loop         mov       t0,#3                   'setup the number of passes for the current memory location.
              mov       t1,pattern              'setup the starting test pattern.

:do_pass      wrlong    t1,curr_addy            'write a test pattern out to Hub RAM
              rdlong    t2,curr_addy            'read the value back out of Hub RAM
              cmp       t2,t1              wz   'compare the written value with the read value
    if_nz     jmp       #Do_test_fail           'if the two are different, the result will be non-zero and the test failed.

              ror       t1,#1                   'rotate the current test pattern
              xor       t1,pattern              'flip the bits of the test pattern to form a new test pattern
              
              djnz      t0,#:do_pass            'do another test pass on the current memory location?

              add       curr_addy,#4            'move on to the next memory location.
              djnz      num_longs,#:loop        'test another memory location, testing from low memory to high memory

              'if we get here, cog's access to Hub RAM passed!

              mov       a0,#pass_str            'setup to print pass string
              call      #Print_str              'print the pass string to the terminal
                            
Do_Test_ret   ret                               'all done, return
              

'================================ Do Test Fail ==================================================================
'       This routine is called when the value read back from Hub RAM differs from what was just written.  This
'       indicates a memory failure.

Do_test_fail  mov       a0,#fail_str            'setup the "fail" string
              call      #Print_str              'print the "fail" string
              jmp       #hang                   'the test failed, hang the cog up!
              

'================================ Hang ==========================================================================
'       Hang the cog's execution.  This routine simply stops the cog.

hang          cogstop  cog_num                  'stop self
              jmp       #hang                   'shouldnt ever have to jump back, but just in case...


'================================ Print String ==================================================================

Print_Str     movd      :strloop,a0
              nop
:strloop      cmp       0-0,#0            wz    'check to see if we have reached our zero-terminator
        

              'xor      outa, #1                'toggle an LED (for debugging)

              movs      :load,a0
              nop
              nop
        if_z  jmp       #Print_Str_ret          'if we have, then jump to the return address
        
:load         mov       acc,0-0                 'load the char into the accumulator for transmission
              call      #Serout                 'send the char
              
              add       a0,#1                   'increment the string address pointer
              movd      :strloop,a0             'set up the next char to be compared
              nop                               'let a few "do-nothing" instructions through to allow the destination
              nop                               'register modificaiton take place.
              jmp       #:strloop               'do it again

Print_Str_ret
              ret

'================================ Serout =======================================================================

Serout        or        outa,out_mask           'set TX_PIN high for serial transmission
              or        dira,out_mask           'Set TX_PIN to output
              mov       bit_cnt,#10             'extra stop bit padding, set up the number of bits to be TXed
              mov       t0,#%1                  'extra stop bit padding
              shl       t0,#8                   'get ready to OR this with the data byte
              or        acc,t0                  'OR it
              shl       acc,#1                  'shift in one start bit

              mov       bit_time,baud           'calc the number of clock ticks for our baud rate
              add       bit_time,cnt            'add to the current system time
              
:bitloop      shr       acc,#1            wc    'put bit to be TXed into carry
              muxc      outa,out_mask           'stick the state of carry to the pin
              waitcnt   bit_time,baud           'wait for the right amount of time
              djnz      bit_cnt,#:bitloop       'do it again until we run out of bits to TX
              
              andn      dira,out_mask           'make the pin an input to let the other cogs have a chance at communicating
Serout_ret    ret


'================================ Blink LEDs ===================================================================
'
'  This little routine blinks one LED on the quickstart board, per cog. This is, cog 0 only blinks
'  LED 0 located on pin P16.  Cog 1 only blinks LED 1 on pin P17 and so on.  This blinks the LEDs in
'  an infinite loop, so real code execution stops upon jumping to this routine.

'  This routine could potentially be used to report back to the Propeller Tester success/failure of each cog.


blink_leds    cogid     t0                      'save the cog's ID in t0, used for determining the LED pin mask
              add       t0,#16                  'starting at pin 16, this is for the Quickstart's LEDs.

              mov       t1,#1                   'setup the pin mask
              shl       t1,t0                   'shift the single bit out
              
              
              or        dira,t1                 'make pin an output

              waitcnt   sync_start,#0           'synchronize the LED blinking in each cog.
                                                'This value was set up near the beginning of the program.

              mov       timer,one_sec           'set up the one second blink timer
              add       timer,cnt               'get the current time
              
:blink        xor       outa,t1                 'toggle pin state
              waitcnt   timer,one_sec           'wait one second
              jmp       #:blink                 'do it again


'================================ Variable Definitions =========================================================


one_sec       long      _clkfreq                'number of clock ticks in one second
sync_start    long      0-0                     'written by the spin init code

num_longs     long      $8000>>2                'the number of longs in Hub RAM
pattern       long      $5555_5555              'the starting bit pattern used to read/write to Hub RAM

out_mask      long      1<<TX_PIN               'output pin mask

'Simple string definitions.  Each character byte is stored as a long to simplify the transmission code.
cog_num_str   long      "Cog ", 0
pass_str      long      ": PASS", 0
fail_str      long      ": !!!!  FAIL", 0

baud          long      _clkfreq / BAUDRATE     'number of clock ticks to acheive a specified baud rate


'================================ Uninitialized Cog Variable Definitions =======================================

acc           res       1
bit_time      res       1
bit_cnt       res       1
cog_num       res       1
curr_addy     res       1
timer         res       1

'temporaries
t0            res       1
t1            res       1
t2            res       1
t3            res       1

'return registers
r0            res       1
r1            res       1

'argument registers
a0            res       1
a1            res       1